Option Explicit

'################################################################
' 创建列对象
' 仅供 IDFFilterCol 接口的实现类调用，外部不应该主动调用
'################################################################
' ==================================
' 生成基础运算函数列
' ==================================
' 以指定 FilterCol 为基础创建一个加法计算列
'
' @param c 需要包装的列对象
' @param val 计算参数
'            可以是基础类型、IDFFilterCol类型(和另一个列进行计算)
' @return 加法计算列
Public Function unsafeCreateAddCol (ByRef c As IDFFilterCol, ByRef val As Variant) As IDFFilterCol
    If TypeOf val Is IDFFilterCol Then
        ' 创建多列计算列
        Dim doubleComputeCol As DFFilterDoubleColAdd
        Set doubleComputeCol = New DFFilterDoubleColAdd

        Dim rightCol As IDFFilterCol
        Set rightCol = val
        doubleComputeCol.Init c, rightCol

        Set unsafeCreateAddCol = doubleComputeCol
    Else
        ' 创建单列计算列
        Dim singleComputeCol As DFFilterColAdd
        Set singleComputeCol = New DFFilterColAdd
        singleComputeCol.Init c, val

        Set unsafeCreateAddCol = singleComputeCol
    End If
End Function

' 以指定 FilterCol 为基础创建一个减法计算列
'
' @param c 需要包装的列对象
' @param val 计算参数
'            可以是基础类型（只对 INT、DECIMAL 类型有效，其他类型抛出异常）、
'            IDFFilterCol类型(和另一个列进行计算)
' @return 减法计算列
Public Function unsafeCreateSubtractCol(ByRef c As IDFFilterCol, ByRef val As Variant) As IDFFilterCol
    If TypeOf val Is IDFFilterCol Then
        ' 创建多列计算列
        Dim doubleComputeCol As DFFilterDoubleColSubtract
        Set doubleComputeCol = New DFFilterDoubleColSubtract

        Dim rightCol As IDFFilterCol
        Set rightCol = val
        doubleComputeCol.Init c, rightCol

        Set unsafeCreateSubtractCol = doubleComputeCol
    Else
        ' 创建单列计算列
        Dim singleComputeCol As DFFilterColSubtract
        Set singleComputeCol = New DFFilterColSubtract
        singleComputeCol.Init c, val

        Set unsafeCreateSubtractCol = singleComputeCol
    End If
End Function

' 以指定 FilterCol 为基础创建一个乘法计算列
'
' @param c 需要包装的列对象
' @param val 计算参数
'            可以是基础类型（只对 INT、DECIMAL 类型有效，其他类型抛出异常）、
'            IDFFilterCol类型(和另一个列进行计算)
' @return 乘法计算列
Public Function unsafeCreateMultiplyCol(ByRef c As IDFFilterCol, ByRef val As Variant) As IDFFilterCol
    If TypeOf val Is IDFFilterCol Then
        ' 创建多列计算列
        Dim doubleComputeCol As DFFilterDoubleColMultiply
        Set doubleComputeCol = New DFFilterDoubleColMultiply

        Dim rightCol As IDFFilterCol
        Set rightCol = val
        doubleComputeCol.Init c, rightCol

        Set unsafeCreateMultiplyCol = doubleComputeCol
    Else
        ' 创建单列计算列
        Dim singleComputeCol As DFFilterColMultiply
        Set singleComputeCol = New DFFilterColMultiply
        singleComputeCol.Init c, val

        Set unsafeCreateMultiplyCol = singleComputeCol
    End If
End Function

' 以指定 FilterCol 为基础创建一个除法计算列
'
' @param c 需要包装的列对象
' @param val 计算参数
'            可以是基础类型（只对 INT、DECIMAL 类型有效，其他类型抛出异常）、
'            IDFFilterCol类型(和另一个列进行计算)
' @return 除法计算列
Public Function unsafeCreateDivideCol(ByRef c As IDFFilterCol, ByRef val As Variant) As IDFFilterCol
    If TypeOf val Is IDFFilterCol Then
        ' 创建多列计算列
        Dim doubleComputeCol As DFFilterDoubleColDivide
        Set doubleComputeCol = New DFFilterDoubleColDivide

        Dim rightCol As IDFFilterCol
        Set rightCol = val
        doubleComputeCol.Init c, rightCol

        Set unsafeCreateDivideCol = doubleComputeCol
    Else
        ' 创建单列计算列
        Dim singleComputeCol As DFFilterColDivide
        Set singleComputeCol = New DFFilterColDivide
        singleComputeCol.Init c, val

        Set unsafeCreateDivideCol = singleComputeCol
    End If
End Function

' 以指定 FilterCol 为基础创建一个字符串连接计算列
'
' @param c 需要包装的列对象
' @param val 计算参数
'            可以是基础类型（必须是字符串）、IDFFilterCol类型(和另一个列进行计算)
' @return 字符串连接计算列
Public Function unsafeCreateConcatCol(ByRef c As IDFFilterCol, ByRef val As Variant) As IDFFilterCol
    If TypeOf val Is IDFFilterCol Then
        ' 创建多列计算列
        Dim doubleComputeCol As DFFilterDoubleColConcat
        Set doubleComputeCol = New DFFilterDoubleColConcat

        Dim rightCol As IDFFilterCol
        Set rightCol = val
        doubleComputeCol.Init c, rightCol

        Set unsafeCreateConcatCol = doubleComputeCol
    Else
        ' 创建单列计算列
        Dim singleComputeCol As DFFilterColConcat
        Set singleComputeCol = New DFFilterColConcat
        singleComputeCol.Init c, val

        Set unsafeCreateConcatCol = singleComputeCol
    End If
End Function

' ==================================
' 生成聚合函数列
' ==================================
' 在指定 FilterCol 的基础上生成最小值计算列
'
' @param c 需要包装的列对象
' @return IDFFilterCol + IDFFilterAggCol 最小值计算列
Public Function unsafeCreateMinCol(ByRef c As IDFFilterCol) As IDFFilterCol
    Dim result As DFFilterColMin
    Set result = New DFFilterColMin

    result.Init c

    Set unsafeCreateMinCol = result
End Function

' 在指定 FilterCol 的基础上生成最大值计算列
'
' @param c 需要包装的列对象
' @return IDFFilterCol + IDFFilterAggCol 最大值计算列
Public Function unsafeCreateMaxCol(ByRef c As IDFFilterCol) As IDFFilterCol
    Dim result As DFFilterColMax
    Set result = New DFFilterColMax

    result.Init c

    Set unsafeCreateMaxCol = result
End Function

' 在指定 FilterCol 的基础上生成求和计算列
'
' @param c 需要包装的列对象
' @return IDFFilterCol + IDFFilterAggCol 求和计算列
Public Function unsafeCreateSumCol(ByRef c As IDFFilterCol) As IDFFilterCol
    Dim result As DFFilterColSum
    Set result = New DFFilterColSum

    result.Init c

    Set unsafeCreateSumCol = result
End Function

' 在指定 FilterCol 的基础上生成求和计算列
'
' @param c 需要包装的列对象
' @return IDFFilterCol + IDFFilterAggCol 平均值计算列
Public Function unsafeCreateAggCol(ByRef c As IDFFilterCol) As IDFFilterCol
    Dim result As DFFilterColAgg
    Set result = New DFFilterColAgg

    result.Init c

    Set unsafeCreateAggCol = result
End Function

'################################################################
' 创建条件对象
' 仅供 IDFFilterCol 接口的实现类调用，外部不应该主动调用
'################################################################
' ==================================
' 条件生成器
' ==================================
' = 运算
'
' @param c 需要包装的列对象
' @param val 比较参数
'            可以是基础类型、IDFFilterCol 类型(和另一个列进行计算)
'            不能是 IDFFilterAggCol 聚合计算列，如果使用了将会抛出异常
Public Function unsafeCreateConditionEq(ByRef c As IDFFilterCol, ByRef val As Variant) As IDFFilterColCondition
    If IsObject(val) Then
        If TypeOf val Is IDFFilterCol Then
            ' 创建多列条件
            Dim rightCol As IDFFilterCol
            Set rightCol = val

            Dim doubleResult As DFFilterRelationColConditionEq
            Set doubleResult = New DFFilterRelationColConditionEq
            doubleResult.Init c, rightCol

            Set unsafeCreateConditionEq = doubleResult
        Else
            ' 抛出异常
            Err.Raise 10314, Description:="Can not create condition with parameter [val], because its type is neither Primitive or IDFFilterCol"
            Exit Function
        End If
    Else
        ' 创建单列条件
        Dim singleResult As DFFilterSingleColConditionEq
        Set singleResult = New DFFilterSingleColConditionEq
        singleResult.Init c, val

        Set unsafeCreateConditionEq = singleResult
    End If
End Function

' != 运算
'
' @param c 需要包装的列对象
' @param val 比较参数
'            可以是基础类型、IDFFilterCol类型(和另一个列进行计算)
'            不能是 IDFFilterAggCol 聚合计算列，如果使用了将会抛出异常
Public Function unsafeCreateConditionNe(ByRef c As IDFFilterCol, ByRef val As Variant) As IDFFilterColCondition
    If IsObject(val) Then
        If TypeOf val Is IDFFilterCol Then
            ' 创建多列条件
            Dim rightCol As IDFFilterCol
            Set rightCol = val

            Dim doubleResult As DFFilterRelationColConditionNe
            Set doubleResult = New DFFilterRelationColConditionNe
            doubleResult.Init c, rightCol

            Set unsafeCreateConditionNe = doubleResult
        Else
            ' 抛出异常
            Err.Raise 10314, Description:="Can not create condition with parameter [val], because its type is neither Primitive or IDFFilterCol"
            Exit Function
        End If
    Else
        ' 创建单列条件
        Dim singleResult As DFFilterSingleColConditionNe
        Set singleResult = New DFFilterSingleColConditionNe
        singleResult.Init c, val

        Set unsafeCreateConditionNe = singleResult
    End If
End Function

' > 运算
'
' @param c 需要包装的列对象
' @param val 比较参数
'            可以是基础类型、IDFFilterCol类型(和另一个列进行计算)
'            不能是 IDFFilterAggCol 聚合计算列，如果使用了将会抛出异常
Public Function unsafeCreateConditionGt(ByRef c As IDFFilterCol, ByRef val As Variant) As IDFFilterColCondition
    If IsObject(val) Then
        If TypeOf val Is IDFFilterCol Then
            ' 创建多列条件
            Dim rightCol As IDFFilterCol
            Set rightCol = val

            Dim doubleResult As DFFilterRelationColConditionGt
            Set doubleResult = New DFFilterRelationColConditionGt
            doubleResult.Init c, rightCol

            Set unsafeCreateConditionGt = doubleResult
        Else
            ' 抛出异常
            Err.Raise 10314, Description:="Can not create condition with parameter [val], because its type is neither Primitive or IDFFilterCol"
            Exit Function
        End If
    Else
        ' 创建单列条件
        Dim singleResult As DFFilterSingleColConditionGt
        Set singleResult = New DFFilterSingleColConditionGt
        singleResult.Init c, val

        Set unsafeCreateConditionGt = singleResult
    End If
End Function

' < 运算
'
' @param c 需要包装的列对象
' @param val 比较参数
'            可以是基础类型、IDFFilterCol类型(和另一个列进行计算)
'            不能是 IDFFilterAggCol 聚合计算列，如果使用了将会抛出异常
Public Function unsafeCreateConditionLt(ByRef c As IDFFilterCol, ByRef val As Variant) As IDFFilterColCondition
    If IsObject(val) Then
        If TypeOf val Is IDFFilterCol Then
            ' 创建多列条件
            Dim rightCol As IDFFilterCol
            Set rightCol = val

            Dim doubleResult As DFFilterRelationColConditionLt
            Set doubleResult = New DFFilterRelationColConditionLt
            doubleResult.Init c, rightCol

            Set unsafeCreateConditionLt = doubleResult
        Else
            ' 抛出异常
            Err.Raise 10314, Description:="Can not create condition with parameter [val], because its type is neither Primitive or IDFFilterCol"
            Exit Function
        End If
    Else
        ' 创建单列条件
        Dim singleResult As DFFilterSingleColConditionLt
        Set singleResult = New DFFilterSingleColConditionLt
        singleResult.Init c, val

        Set unsafeCreateConditionLt = singleResult
    End If
End Function

' >= 运算
'
' @param c 需要包装的列对象
' @param val 比较参数
'            可以是基础类型、IDFFilterCol类型(和另一个列进行计算)
'            不能是 IDFFilterAggCol 聚合计算列，如果使用了将会抛出异常
Public Function unsafeCreateConditionGe(ByRef c As IDFFilterCol, ByRef val As Variant) As IDFFilterColCondition
    If IsObject(val) Then
        If TypeOf val Is IDFFilterCol Then
            ' 创建多列条件
            Dim rightCol As IDFFilterCol
            Set rightCol = val

            Dim doubleResult As DFFilterRelationColConditionGe
            Set doubleResult = New DFFilterRelationColConditionGe
            doubleResult.Init c, rightCol

            Set unsafeCreateConditionGe = doubleResult
        Else
            ' 抛出异常
            Err.Raise 10314, Description:="Can not create condition with parameter [val], because its type is neither Primitive or IDFFilterCol"
            Exit Function
        End If
    Else
        ' 创建单列条件
        Dim singleResult As DFFilterSingleColConditionGe
        Set singleResult = New DFFilterSingleColConditionGe
        singleResult.Init c, val

        Set unsafeCreateConditionGe = singleResult
    End If
End Function

' <= 运算
'
' @param c 需要包装的列对象
' @param val 比较参数
'            可以是基础类型、IDFFilterCol类型(和另一个列进行计算)
'            不能是 IDFFilterAggCol 聚合计算列，如果使用了将会抛出异常
Public Function unsafeCreateConditionLe(ByRef c As IDFFilterCol, ByRef val As Variant) As IDFFilterColCondition
    If IsObject(val) Then
        If TypeOf val Is IDFFilterCol Then
            ' 创建多列条件
            Dim rightCol As IDFFilterCol
            Set rightCol = val

            Dim doubleResult As DFFilterRelationColConditionLe
            Set doubleResult = New DFFilterRelationColConditionLe
            doubleResult.Init c, rightCol

            Set unsafeCreateConditionLe = doubleResult
        Else
            ' 抛出异常
            Err.Raise 10314, Description:="Can not create condition with parameter [val], because its type is neither Primitive or IDFFilterCol"
            Exit Function
        End If
    Else
        ' 创建单列条件
        Dim singleResult As DFFilterSingleColConditionLe
        Set singleResult = New DFFilterSingleColConditionLe
        singleResult.Init c, val

        Set unsafeCreateConditionLe = singleResult
    End If
End Function

' in() 运算
'
' @param c 需要包装的列对象
' @param vs 比较参数数组
'            可以是基础类型、IDFFilterCol类型(和另一个列进行计算)
'            不能是 IDFFilterAggCol 聚合计算列，如果使用了将会抛出异常
Public Function unsafeCreateConditionInVals(ByRef c As IDFFilterCol, ByRef vs As Variant) As IDFFilterColCondition
    ' 检查所有的参数，如果：
    '   全都是基础数据类型，则创建单列条件
    '   全部都是列对象，则创建多列条件
    '   既有基础数据类型，又有列对象，则需要既爱那个所有基础类型数据转换成字面量列，然后生成多列条件

    ' 1. 检查参数
    ' 1.1 如果参数 val 不是数组，则抛出异常
    If IsArray(vs) = False Then
        Err.Raise 10005, Description:="parameter vs must be Array"
        Exit Function
    End If

    ' 1.2 检查 vs 的数量
    Dim vsIdxMin As Integer
    Dim vsIdxMax As Integer
    Dim vsSize As Integer
    vsIdxMin = LBound(vs)
    vsIdxMax = UBound(vs)
    vsSize = vsIdxMax - vsIdxMin + 1
    If vsSize = 0 Then
        Err.Raise 10006, Description:="Array parameter vs's size must not be 0"
        Exit Function
    End If

    ' 2. 检查数据类型
    ' 标记 vs 的元素是否全部是基础数据类型
    Dim isAllPrimitive As Boolean
    ' 标记 vs 的元素是否全部是列对象
    Dim isAllColObj As Boolean
    isAllPrimitive = True
    isAllColObj = True

    Dim v As Variant
    Dim i As Integer
    i = 0
    '    通过 For Each 来避免对是否使用 Set 来接受数组元素的判断
    For Each v In vs
        If IsObject(v) Then
            ' 如果是对象类型，则
            '   不能是 Nothing
            '   必须是 IDFFilterCol 类型
            If v Is Nothing Then
                Err.Raise 10001, Description:="parameter vs(" & (i + vsIdxMin) & ") must be not nothing"
                Exit Function
            End If
            If Not TypeOf v Is IDFFilterCol Then
                Err.Raise 10314, Description:="Can not create condition with parameter [v(" & (i + vsIdxMin) & ")], because its type is neither Primitive or IDFFilterCol"
                Exit Function
            End If

            isAllPrimitive = isAllPrimitive And False
            isAllColObj = isAllColObj And True
        Else
            ' 如果是基础对象类型，则必须不能是 Empty
            If IsEmpty(v) Then
                Err.Raise 10002, Description:="parameter vs(" & (i + vsIdxMin) & ") must be not empty"
                Exit Function
            End If

            isAllPrimitive = isAllPrimitive And True
            isAllColObj = isAllColObj And False
        End If

        i = i + 1
    Next

    ' 3. 根据标记的组合确认需要创建的条件类型
    If isAllPrimitive = True And isAllColObj = False Then
        ' 3.1 vs 的全部元素都是基础数据类型，创建单列条件
        Dim singleResult As DFFilterSingleColCondInVals
        Set singleResult = New DFFilterSingleColCondInVals
        singleResult.Init c, vs

        Set unsafeCreateConditionInVals = singleResult
        Exit Function
    End If

    If isAllPrimitive = False And isAllColObj = True Then
        ' 3.2 vs 的全部元素都是列对象，创建多列条件
        Dim doubleResult As DFFilterRelationColCondInVals
        Set doubleResult = New DFFilterRelationColCondInVals
        doubleResult.Init c, vs

        Set unsafeCreateConditionInVals = doubleResult
        Exit Function
    End If

    ' 3.3 vs 中既有基础类型数据，又有列对象，需要将所有基础类型数据转换成字面量列
    '     到这里不需要再做空检验，因为在 2 中已经检查完了，不会再出现 Empty 或 Nothing 的情况
    Dim tempLiteralCol As DFFilterLiteralCol
    Dim colVs() As Variant
    ReDim colVs(0 To (vsSize - 1)) As Variant
    i = 0
    For Each v In vs
        If IsObject(v) Then
            Set colVs(i) = v
        Else
            Set tempLiteralCol = New DFFilterLiteralCol
            tempLiteralCol.Init v

            Set colVs(i) = tempLiteralCol
        End If

        i = i + 1
    Next

    ' 创建多列条件
    Dim mixedResult As DFFilterRelationColCondInVals
    Set mixedResult = New DFFilterRelationColCondInVals
    mixedResult.Init c, colVs

    Set unsafeCreateConditionInVals = mixedResult
End Function

' between 运算
'
' @param c 需要包装的列对象
' @param left 左值
' @param right 右值
'            可以是基础类型、IDFFilterCol类型(和另一个列进行计算)
'            不能是 IDFFilterAggCol 聚合计算列，如果使用了将会抛出异常
Public Function unsafeCreateConditionBetween(ByRef c As IDFFilterCol, ByRef left As Variant, ByRef right As Variant) As IDFFilterColCondition
    ' 如果 left + right 都是基础数据类型，则创建一个单列条件
    ' 如果 left + right 都是列对象，则创建一个多列条件
    ' 如果 left + right 中一个是列对象，另一个是基础类型，则将基础类型转换为字面量列后再创建多列条件

    If Not IsObject(left) And Not IsObject(right) Then
        ' 全部是基础类型数据，创建单列条件
        Dim result1 As DFFilterSingleColCondBetween
        Set result1 = New DFFilterSingleColCondBetween

        ' left + right 必须不能是 Empty
        If IsEmpty(left) Then
            Err.Raise 10002, Description:="parameter left must be not empty"
            Exit Function
        End If
        If IsEmpty(right) Then
            Err.Raise 10002, Description:="parameter right must be not empty"
            Exit Function
        End If

        result1.Init c, left, right

        Set unsafeCreateConditionBetween = result1
        Exit Function
    End If

    ' 将 left + right 中的基础数据类型转换成字面量列，然后生成多列条件
    Dim leftCol As IDFFilterCol
    Dim rightCol As IDFFilterCol
    If IsObject(left) Then
        If left Is Nothing Then
            Err.Raise 10001, Description:="parameter left must be not nothing"
            Exit Function
        End If

        If Not TypeOf left Is IDFFilterCol Then
            Err.Raise 10314, Description:="Can not create condition with parameter [left], because its type is neither Primitive or IDFFilterCol"
            Exit Function
        End If

        Set leftCol = left
    Else
        ' left 必须不能是 Empty
        If IsEmpty(left) Then
            Err.Raise 10002, Description:="parameter left must be not empty"
            Exit Function
        End If

        Dim leftLiteralCol As DFFilterLiteralCol
        Set leftLiteralCol = New DFFilterLiteralCol
        leftLiteralCol.Init left

        Set leftCol = leftLiteralCol
    End If

    If IsObject(right) Then
        If right Is Nothing Then
            Err.Raise 10001, Description:="parameter right must be not nothing"
            Exit Function
        End If

        If Not TypeOf right Is IDFFilterCol Then
            Err.Raise 10314, Description:="Can not create condition with parameter [right], because its type is neither Primitive or IDFFilterCol"
            Exit Function
        End If

        Set rightCol = right
    Else
        ' right 必须不能是 Empty
        If IsEmpty(right) Then
            Err.Raise 10002, Description:="parameter right must be not empty"
            Exit Function
        End If

        Dim rightLiteralCol As DFFilterLiteralCol
        Set rightLiteralCol = New DFFilterLiteralCol
        rightLiteralCol.Init right

        Set rightCol = rightLiteralCol
    End If

    ' 生成多列条件
    Dim result2 As DFFilterRelationColCondBetween
    Set result2 = New DFFilterRelationColCondBetween
    result2.Init c, leftCol, rightCol

    Set unsafeCreateConditionBetween = result2
End Function
